import { assetManager, ImageAsset, math, sp, Texture2D } from "cc";
import Instance from "../Utils/Instance";
import * as fgui from "fairygui-cc";
import UrlEnum from "./UrlEnum";
import TimerMgr from "../Timer/TimerMgr";

export default class SpineLoader {
    private _spineDict: Object = {};
    private _callbackList: SkeletonDataHandler[] = [];
    private _isloader: boolean = false;
    public static get ins(): SpineLoader {
        return Instance.get(SpineLoader);
    }
    public console(): void {
        console.clear();
        console.log(this._spineDict);
    }
    public setSpine(loader: fgui.GLoader3D, url: string, animationName: string, skin?: string, loop: boolean = true) {
        if (window[`TrackEntryListeners`]) {
            window[`TrackEntryListeners`]._listenerSet.clear();
        }
        if (!loader) {
            console.log("loader不存在");
            return;
        }
        loader[`_container`].destroyAllChildren();
        if (this._spineDict[url]) {
            // console.log("已加载过", loader.name, url);
            loader.animationName = animationName;
            if (skin) {
                loader.skinName = skin;
            }
            loader.loop = loop;
            loader.setSpine(this._spineDict[url] as sp.SkeletonData, new math.Vec2(0, 0));
            return;
        }
        // if (this.getisLoader(url)) {
        //     console.log("正在加载中", url);
        //     return;
        // }
        var handler: SkeletonDataHandler = new SkeletonDataHandler(url, (sp: sp.SkeletonData, h: SkeletonDataHandler) => {
            if (!h.gloader3d || !h.gloader3d.parent) {
                return;
            }
            h.gloader3d.animationName = h.animationName;
            if (h.skin) {
                h.gloader3d.skinName = h.skin;
            }
            h.gloader3d.loop = h.loop;
            // console.log("加载完成设置：", sp);
            h.gloader3d.setSpine(sp, new math.Vec2(0, 0));
        }, this);
        handler.gloader3d = loader;
        handler.animationName = animationName;
        handler.skin = skin;
        handler.loop = loop;
        this._callbackList.push(handler);
        if (this._isloader) {
            // console.log("%c正在加载中" + url, `color:red`);
            return;
        }
        this.loadSpine();
    }

    private loadSpine(): void {
        if (this._callbackList.length <= 0) {
            this._isloader = false;
            return;
        }

        this._isloader = true;
        var handler: SkeletonDataHandler = this._callbackList.shift();
        if (this._spineDict[handler.url]) {
            if (!handler.gloader3d || !handler.gloader3d.parent) {
                return;
            }
            handler.gloader3d.animationName = handler.animationName;
            if (handler.skin) {
                handler.gloader3d.skinName = handler.skin;
            }
            handler.gloader3d.loop = handler.loop;
            handler.gloader3d.setSpine(this._spineDict[handler.url] as sp.SkeletonData, new math.Vec2(0, 0));
            this._isloader = false;
            this.loadSpine();
            return;
        };
        var image = `${UrlEnum.spineUrl}${handler.url}.png`;
        var ske = `${UrlEnum.spineUrl}${handler.url}.json`;
        var atlas = `${UrlEnum.spineUrl}${handler.url}.atlas`;
        // console.log("开始加载spine", image, ske, atlas);
        assetManager.loadAny([{ url: atlas, ext: '.txt' }, { url: ske, ext: '.txt' }], (error, assets) => {
            assetManager.loadRemote(image, (error, img: ImageAsset) => {
                if (error) {
                    this._isloader = false;
                    this.loadSpine();
                    return;
                }
                var texture: Texture2D = new Texture2D();
                texture.image = img;
                var asset: sp.SkeletonData = new sp.SkeletonData();
                asset.skeletonJson = assets[1];
                asset.atlasText = assets[0];
                asset.textures = [texture];
                asset.textureNames = [`${handler.url}.png`];
                asset._uuid = `${handler.url}`;
                this._spineDict[handler.url] = asset;
                // console.log("加载完成spine", asset);
                handler.apply(asset);
                this._isloader = false;
                this.loadSpine();
            });
        });
    }
    public freespine(url): void {
        if (this._spineDict[url]) {
            assetManager.releaseAsset(this._spineDict[url].textures[0]);
            assetManager.releaseAsset(this._spineDict[url].atlasText);
            assetManager.releaseAsset(this._spineDict[url]);
            delete this._spineDict[url];
        }
    }
    private getisLoader(url: string): boolean {
        return this._callbackList.some(i => i.url === url);
    }
    public spinePreload(url: string): void {
        let image = `${UrlEnum.spineUrl}${url}.png`;
        let ske = `${UrlEnum.spineUrl}${url}.json`;
        let atlas = `${UrlEnum.spineUrl}${url}.atlas`;
        assetManager.loadAny([{ url: atlas, ext: '.txt' }, { url: ske, ext: '.txt' }], (error, assets) => {
            assetManager.loadRemote(image, (error, img: ImageAsset) => {
                if (error) {
                    return;
                }
                const texture: Texture2D = new Texture2D();
                texture.image = img;
                const asset: sp.SkeletonData = new sp.SkeletonData();
                asset.skeletonJson = assets[1];
                asset.atlasText = assets[0];
                asset.textures = [texture];
                asset.textureNames = [`${url}.png`];
                asset._uuid = `${url}`;
                this._spineDict[url] = asset;
            });
        });
    }
}

export class SkeletonDataHandler {

    public url: string;
    public callback: Function;
    public thisObj: any;
    public gloader3d: fgui.GLoader3D;
    public animationName: string;
    public skin: string;
    public loop: boolean;
    public constructor(url: any, callback: Function, thisObj: any) {
        this.url = url;
        this.callback = callback;
        this.thisObj = thisObj;
    }

    public apply(sp: any): void {
        this.callback.call(this.thisObj, sp, this);
    }
}